module(..., package.seeall)
require'cosmo'
require 'zdc.doc'
local to_doc=zdc.doc.get_doc
local to_string=zdc.doc.to_string

templ={}
templ.proto= [==[
<h1><a name=$REF>$NAME</h1>
<p>
<a name=$PROTOREF>
prototype: $PROTO
</p>
<p>
$DESC
</p>
<h3>ACL</h3>
<pre>
$ACL
</pre>
$FIELDS
$SUBS
$ACTS
]==]
templ.proto_field = [==[
<h2>$NAME</h2>
<table>
 <thead>
  <tr>
   <th width="20px">IDX</th>
   <th>ID</th>
   <th>NAME</th>
   <th>TYPE</th>
   <th>LIMIT</th>
   <th>DEFAULT</th>
   <th>DEFAULT_STORE</th>
   <th>RO</th>
   <th>VIR</th>
   <th>HIDE</th>
   <th>PROTO</th>
   <th>ACT</th>
   <th>DOC</th>
  </tr>
 </thead>
 $do_row[[
  <tr>
   <td>$idx</td>
   <td>$id</td>
   <td>$name</td>
   <td><a href=#$type>$type</td>
   <td>$limit</td>
   <td>$defval</td>
   <td>$defval2</td>
   <td>$ro</td>
   <td>$vir</td>
   <td>$hide</td>
   <td>$proto</td>
   <td>$activate</td>
   <td><pre>$doc</pre></td>
  </tr>
 ]]
</table>
]==]

templ.proto_field_dy = [==[
<h2>$NAME</h2>
<table>
 <thead>
  <tr>
   <th>PATTERN</th>
   <th>LEN</th>
   <th width="20px">IDX</th>
   <th>NAME</th>
   <th>TYPE</th>
   <th>LIMIT</th>
   <th>RO</th>
   <th>VIR</th>
   <th>HIDE</th>
   <th>PROTO</th>
   <th>ACT</th>
   <th>DOC</th>
  </tr>
 </thead>
 $do_row[[
  <tr>
   <td>$dyid</td>
   <td>$dylimit</td>
   <td>$idx</td>
   <td>$name</td>
   <td>$type</td>
   <td>$limit</td>
   <td>$ro</td>
   <td>$vir</td>
   <td>$hide</td>
   <td>$proto</td>
   <td>$activate</td>
   <td><pre>$doc</pre></td>
  </tr>
 ]]
</table>
]==]

templ.proto_sub = [==[
<h2>$NAME</h2>
<table>
 <thead>
  <tr>
   <th>ID</th>
   <th>NAME</th>
   <th>FIXED</th>
   <th>CONT</th>
   <th>MC</th>
   <th>RO</th>
   <th>VIR</th>
   <th>HIDE</th>
   <th>DOC</th>
  </tr>
 </thead>
 $do_row[[
  <tr>
   <td>$id</td>
   <td>$name</td>
   <td>$fixed</td>
   <td>$cont</td>
   <td>$mc</td>
   <td>$ro</td>
   <td>$vir</td>
   <td>$hide</td>
   <td><pre>$doc</pre></td>
  </tr>
 ]]
</table>
]==]

templ.proto_sub_dy = [==[
<h2>$NAME</h2>
<table>
 <thead>
  <tr>
   <th>PATTERN</th>
   <th>LEN</th>
   <th>NAME</th>
   <th>CONT</th>
   <th>MC</th>
   <th>RO</th>
   <th>VIR</th>
   <th>HIDE</th>
   <th>DOC</th>
  </tr>
 </thead>
 $do_row[[
  <tr>
   <td>$dyid</td>
   <td>$dylimit</td>
   <td>$name</td>
   <td>$cont</td>
   <td>$mc</td>
   <td>$ro</td>
   <td>$vir</td>
   <td>$hide</td>
   <td><pre>$doc</pre></td>
  </tr>
 ]]
</table>
]==]


templ.proto_act = [==[
<h2>$NAME</h2>
<table>
 <thead>
  <tr>
   <th>ID</th>
   <th>NAME</th>
   <th>ASYNC</th>
   <th>ENV</th>
   <th>HIDE</th>
   <th>DOC</th>
  </tr>
 </thead>
 $do_row[[
  <tr>
   <td>$id</td>
   <td>$name</td>
   <td>$async</td>
   <td>$env</td>
   <td>$hide</td>
   <td><pre>$doc</pre></td>
  </tr>
 ]]
</table>
]==]

templ.type_table=[==[
<h2><a name=$REF>$NAME</h2>
<pre>$DISP</pre>
<pre>$DESC</pre>
<pre>$EXAM</pre>
$IFSTR[=[
<pre>$HELPU</pre>
]=]
$IFTABLE[=[
<table>
 <thead>
  <tr>
   <th>KEY</th>
   <th>VAL</th>
   <th>DISP</th>
   <th>DESC</th>
  </tr>
 </thead>
 $do_row[[
  <tr>
   <td>$v1</td>
   <td>$v2</td>
   <td>$v3</td>
   <td><pre>$v4</pre></td>
  </tr>
 ]]
</table>
]=]
]==]
function gen_one_proto_field(node,tsp)
	local fds=node:__get_ordered_fields_name_lvl()
	if #fds==0 then return '' end
	local function decorate_fd(n,fd)
	if type(fd[2])=='table' then tsp[fd[2]]=true end
	local ddfav
	if node[n]~=nil and type(fd[2])=='table' and fd[2]._showS then ddfav=fd[2]:_showS(node[n]) end	
	
	return {
			idx   = fd[1],
            id   = n,            
            name = to_doc(fd._doc,'disp') or '',
            type = to_doc(fd[2],'name') or 'NO NAME',
            limit  = to_string(fd[3] or ''),
            defval=ddfav or node[n] or (node[n]==false and 'false') or '',
            defval2=node[n] or (node[n]==false and 'false') or '',
            ro=fd.ro or '',
            vir=fd.vir or '',
            hide=fd.hide or '',
            proto=fd.proto or '',
            activate=fd.activate or '',
            doc    = to_doc(fd._doc,'desc') or '',
      }      
   end

   return cosmo.f(templ.proto_field){
	NAME='fields',
    do_row   = function()
                 for i, f in ipairs(fds) do
                 	local fd=node:__get_field_define(f)
                 	if fd.proto~='sub' then
                    	cosmo.yield(decorate_fd(f,fd))
                    end
                 end                 
              end,
  }
end

function gen_one_proto_field_dy(node,tsp)
	if not node._dfs then return '' end
	local function decorate_fd(n,fd)
	local dyid=(type(n[1])=='string' and n[1]) or to_doc(n[1],'name') or 'NO NAME'
	if type(n[1])=='table' then
		tsp[n[1]]=true
		dyid = '<a href=#' .. (to_doc(n[1],'name') or 'NO NAME') .. '>' .. dyid 
	end
	if type(fd[2])=='table' then tsp[fd[2]]=true end
	
     return {
     		dyid=dyid,
     		dylimit=to_string(n[2] or ''),
      		idx   = fd[1],
            name = to_doc(fd._doc,'disp') or '',
            type = to_doc(fd[2],'name') or '',
            limit  = to_string(fd[3] or ''),
            ro=fd.ro or '',
            vir=fd.vir or '',
            hide=fd.hide or '',
            proto=fd.proto or '',
            activate=fd.activate or '',
            doc    = to_doc(fd._doc,'desc') or '',
      }      
   end

   return cosmo.f(templ.proto_field_dy){
	NAME='dynamic fields',
    do_row   = function()
                 --_array
                 local dfs=node._dfs
                 if dfs._array then
                 	local t=decorate_fd({'_array',dfs._array[1]},dfs._array[2])
                 	cosmo.yield(t)
                 end
                 if dfs._patterns then
                 	for _,i in ipairs(dfs._patterns) do
                 		local t=decorate_fd(i,i[3])
                 		cosmo.yield(t)
                 	end
                 end
              end,
  }
end
function gen_one_proto_fds(node,tsp)
	return gen_one_proto_field(node,tsp) ..  gen_one_proto_field_dy(node,tsp)
end

local function mctostr(mc)
	local s={}
	local p={}
	if not mc then return nil,nil end
	for i,j in pairs(mc) do
		s[#s+1]= i .. ':' ..  j
		p[#p+1]=j
	end
	return table.concat(p,','),table.concat(s,',')
end
local function tablenumitems(s)
	local i=0
	if not s then return i end
	for _,_ in pairs(s) do
		i=i+1
	end
	return i
end
local function mergeusp_subn(usp,node)
	usp=usp or {}
	local all=node:__get_child_all()
	for k,v in pairs(all) do
		if not usp[v.__id] then usp[v.__id]=true end
	end
	return usp
end
local function mergeusp(usp,cont,mc)
	usp=usp or {}
	if mc then
		local p=string.match(cont,"^.-_pto%s-=%s-['\"](.-)['\"]")
		for i,j in pairs(mc) do
			local jj=p:gsub('~mc~',j)
			if not usp[jj] then usp[jj]=true end
		end
	else
		local p=string.match(cont,"^.-_pto%s-=%s-['\"](.-)['\"]")
		if p and not usp[p] then usp[p]=true end
	end
	return usp
end
local function formathrefproto(p,t)
	if t==1 then
		-- _pto='?'
		local pid=string.match(p,"^.-_pto%s-=%s-['\"](.-)['\"]")
		if pid then
			return '<a href=#' .. pid .. '>' .. p
		else
			return ''
		end
	elseif t==2 then
		-- mc:id,...
		return string.gsub(p,':([^,:]+)','<a href=#%1>%1</a>')
	end
end
function gen_one_proto_sub(node,usp,tsp)
	local fds=node._cds
	if tablenumitems(fds)==0 then return '' end
	if tablenumitems(fds)==1 and fds._patterns then return '' end
	local function decorate_fd(n,fd)
		mergeusp(usp,fd[1],fd._mc)
		local p,s=mctostr(fd._mc)
		local f=''
		local fixref
		local sid=node.__id .. '/' .. n
		-- doc not care the cfd
		if not usp[sid] then usp[sid]=true end
		f=node._cfd and node._cfd[n]
		if f then
			fixref='<a href=#' .. sid .. '>' .. sid
		else
			fixref=formathrefproto(fd[1],1)
		end

		return {
            id   = n,
            name = to_doc(fd._doc,'disp') or '',
            cont = fixref or formathrefproto(fd[1],1),
            fixed= f,
            proto=p or '',
            ro=fd.ro or '',
            vir=fd.vir or '',
            hide=fd.hide or '',
            mc  = formathrefproto(s or '',2),
            doc    = to_doc(fd._doc,'desc') or '',
      }      
   end

   return cosmo.f(templ.proto_sub){
	NAME='subs',
    do_row   = function()
                 for i, f in pairs(fds) do                 	
                 	if i~='_any' and i~='_patterns' then
                    	cosmo.yield(decorate_fd(i,(type(f)=='string' and {f}) or f))
                    end
                 end                 
              end,
  }
end
function gen_one_proto_sub_dy(node,usp,tsp)
	local fds=node._cds
	if not fds or not fds._patterns then return '' end
	local function decorate_fd(fd)
		local n=fd
		mergeusp(usp,fd[3],fd._mc)
		local p,s=mctostr(fd._mc)
		local dyid=(type(n[1])=='string' and n[1]) or to_doc(n[1],'name') or 'NO NAME'
		if type(n[1])=='table' then
			tsp[n[1]]=true 
			dyid = '<a href=#' .. (to_doc(n[1],'name') or 'NO NAME') .. '>' .. dyid
		end
		return {			
			dyid=dyid,
     		dylimit=to_string(n[2] or ''),
            name = to_doc(n._doc,'disp')  or '',
            cont = formathrefproto(n[3],1),
            proto=p or '',
            ro=fd.ro or '',
            vir=fd.vir or '',
            hide=fd.hide or '',
            mc  = formathrefproto(s or '',2),
            doc    = to_doc(fd._doc,'desc') or '',
      }      
   end

   return cosmo.f(templ.proto_sub_dy){
	NAME='dynamic subs',
    do_row   = function()
                 for i, f in pairs(fds._patterns) do                 	
                   	cosmo.yield(decorate_fd(f))
                 end                 
              end,
  }
end
function gen_one_proto_cds(node,usp,tsp)
	return gen_one_proto_sub(node,usp,tsp) .. gen_one_proto_sub_dy(node,usp,tsp)
end

function gen_one_proto_act(node,usp,tsp)
	local fds=node._act
	if tablenumitems(fds)==0 then return '' end
	local function decorate_fd(n,fd)
		return {
			id = n,
			name = to_doc(fd._doc,'disp') or '',
			env=fd.env or '',
			hide=fd.hide or '',
			async=fd.async or '',
			doc = to_doc(fd._doc,'desc') or '',
		}
	end

	return cosmo.f(templ.proto_act){
	NAME='actions',
	do_row   = function()
				for i, f in pairs(fds) do
					cosmo.yield(decorate_fd(i,f))
				end
			end,
	}
end
function gen_one_proto(node,usp,tsp,hi)
	usp[#usp+1]=node.__id
	if hi and node._pto then
		hi[node._pto]=hi[node._pto] or {}
		hi[node._pto][node.__id]={}
	end
	local protohi=''
	local _proto=node.__pto_v
	while _proto and _proto.__id do
		protohi = protohi .. ' <- ' .. _proto.__id
		_proto=_proto.__pto_v  
	end
	mergeusp_subn(usp,node)
	return cosmo.f(templ.proto){
	REF=node.__id,
	NAME=(to_doc(node._doc,'disp')or '') .. '(' .. node.__id .. ')',
	PROTO=protohi,
	PROTOREF=node._pto or '',
	DESC=to_doc(node._doc,'desc')or'',
	ACL=node._acl or '',
	FIELDS=gen_one_proto_fds(node,tsp),
	SUBS=gen_one_proto_cds(node,usp,tsp),
	ACTS=gen_one_proto_act(node,usp,tsp),
	}
end

function gen_one_type(t)
	local disp,desc,exam,hu=zdc.typeex.GetTypeDoc(t)
	return cosmo.f(templ.type_table){REF=t.name,NAME=t.name,DISP=disp or '',DESC=desc or '',EXAM=exam or '',
		IFSTR=cosmo.c(type(hu)=='string'){hu},
		IFTABLE=cosmo.c(type(hu)=='table'){
			do_row   = function()
				for i, f in ipairs(hu or {}) do
					local val='' 
					if type(f)=='table' then
						if t._class=='enum' then
							val=t:_enumval(f[1])or''
						end
						cosmo.yield({v1=f[1] or '',v2=val,v3=f[2] or '',v4=f[3] or ''})
					else
						if t._class=='enum' then
							val=t:_enumval(f)or''	
						end
						cosmo.yield({v1=f or '',v2=val,v3='',v4=''})
					end
				end
			end},
		}
end

